var JSEvent = Object.extend({
	constructor : function(type, detail) {
		this.type = String(type);
		if (arguments.length > 1) {
			this.detail = detail;
		}
	},
	detail : null,
	target : null,
	currentTarget : null,
	propagated : true,
	isPropagationStoped : function() {
		return !this.propagated;
	},
	stopPropagation : function() {
		if (this.propagated !== false) {
			this.propagated = false;
		}
	}
});

var JSEventListener = Object.extend({
	constructor : function(eventType, eventHandler) {
		this._eventType = String(eventType);
		this._eventHandler = eventHandler;
	},
	handleEvent : function(event) {
		if (!(event instanceof JSEvent)) {
			throw new TypeError("JSEventListener::handleEvent()");
		}
		if (!event.isPropagationStoped()) {
			this._eventHandler.call(event.currentTarget, event);
		}
	}
});

var JSEventTarget = Object.extend({
	constructor : function() {
		this._eventListeners = [];
	},
	addEventListener : function(eventType, eventHandler) {
		var eventListener;
		if (eventType instanceof JSEventListener) {
			eventListener = eventType;
			eventType = eventListener._eventType;
			eventHandler = eventListener._eventHandler;
		}
		eventType = String(eventType);
		var eventListeners = this._eventListeners;
		for (var i = 0; i < eventListeners.length; i++) {
			if (eventListeners[i]._eventType === eventType && eventListeners[i]._eventHandler === eventHandler) {
				return;
			}
		}
		if (!(eventListener instanceof JSEventListener)) {
			eventListener = new JSEventListener(eventType, eventHandler);
		}
		eventListeners.push(eventListener);
	},
	removeEventListener : function(eventType, eventHandler) {
		var eventListener;
		if (eventType instanceof JSEventListener) {
			eventListener = eventType;
			eventType = eventListener._eventType;
			eventHandler = eventListener._eventHandler;
		}
		eventType = String(eventType);
		var eventListeners = this._eventListeners;
		for (var i = 0; i < eventListeners.length; i++) {
			if (eventListeners[i]._eventType === eventType && eventListeners[i]._eventHandler === eventHandler) {
				eventListeners.splice(i, 1);
			}
		}
	},
	dispatchEvent : function(type, detail) {
		var event;
		if (type instanceof JSEvent) {
			event = type;
		} else if (arguments.length > 1) {
			event = new JSEvent(type, detail);
		} else {
			event = new JSEvent(type);
		}
		if (!event.target) {
			event.target = this;
		}
		var eventListeners = this._eventListeners;
		for (var i = 0; i < eventListeners.length; i++) {
			if (event.isPropagationStoped()) {
				break;
			}
			if (eventListeners[i]._eventType === event.type) {
				if (event.currentTarget !== this) {
					event.currentTarget = this;
				}
				eventListeners[i].handleEvent(event);
			}
		}
	}
});
